/**
 * 
 */
package de.fsujena.chat_pi.server.listener;

import java.io.*;
import java.net.*;
import java.util.*;

import de.fsujena.chat_pi.connection.RSAException;
import de.fsujena.chat_pi.ressources.Room;
import de.fsujena.chat_pi.ressources.message.Message;
import de.fsujena.chat_pi.ressources.message.SysMessage;
import de.fsujena.chat_pi.ressources.message.TextMessage;
import de.fsujena.chat_pi.ressources.message.SysMessage.CATEGORY;
import de.fsujena.chat_pi.ressources.message.SysMessage.MESSAGE;
import de.fsujena.chat_pi.server.Server;
import de.fsujena.chat_pi.server.userInterface.GraphicalUI;

/**
 * The network listener is used to listen for new clients at a given port. It launches
 * a new message listener, which will be responsible for the direct communication with 
 * the client.
 * 
 * @author Erik Hebestreit
 * @author David Reifert
 * @see MessageListener
 */
public class NetworkListener extends Thread {
	Server serverObject;
	ServerSocket server;
	public List<MessageListener> messageListener = new ArrayList<MessageListener>();
	boolean running=false;
	
	/**
	 * constructor for new network listener to listen at a specific port
	 * 
	 * @param port specifies the port number where the network listener listens
	 */
	public NetworkListener(int port, Server serverObject) throws IOException {
		this.serverObject = serverObject;
		server = new ServerSocket(port);
		server.setSoTimeout(serverObject.prefs.getInt("timeout", 1000));
		running=true;
	}
	
	/**
	 * launch network listener thread
	 */
	public void run() {
		if(serverObject.ui instanceof GraphicalUI){
			serverObject.showHistory();
		}
		while (running) {
			try {
				// wait for a new client to connect to the server and start a new message
				// listener			
				messageListener.add(new MessageListener(server.accept(),serverObject));
				
				// get the index of the element just added (last one) and launch the thread
				messageListener.get(messageListener.size() - 1).start();
			} catch (SocketTimeoutException e) {
			} catch (IOException e) {
				e.printStackTrace();
				serverObject.ui.postError("Error setting up a new connection.");
			} catch (RSAException e) {
				e.printStackTrace();
				serverObject.ui.postError("Error setting up a new RSA encryption.");
			} 
		}
	}
	
	/**
	 * removes connection to client
	 * 
	 * @param user username of the client to be removed
	 */
	public void removeClient(String user){
		if(userExists(user)!=-1){
		Room tempRoom=serverObject.roomManager.getRoom(serverObject.userManager.users.get(user).dir);
		if (tempRoom.userExists(user)){
		tempRoom.userExit(user);
		}
		}
		for(int i=0;i<messageListener.size();i++){
			if(messageListener.get(i).username.equals(user)) {
				try {
					messageListener.remove(i).client.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		SysMessage newUserMessage = new SysMessage(CATEGORY.ROOMMANAGEMENT, MESSAGE.SEND_ROOM_LIST,"SERVER", serverObject.roomManager.foyer.copyRoom());
		newUserMessage.setSent(new Date());
		newUserMessage.setSenderUsername("SERVER");
		if(serverObject.networkListener!=null){
			serverObject.networkListener.distributeMsg(newUserMessage);
		}
		return;
	}
	
	/**
	 * check if the user is connected
	 * @param user	username of the user to be checkt
	 * @return the indexnumber of the user return -1 if the user isn't connected
	 */
	public int userExists(String user){
		for(int i=0;i<messageListener.size();i++){
			if(messageListener.get(i).username.equals(user))
				return i;
		}
		return -1;
	}
	
	/**
	 * check if the user is logged in
	 * @param username 	name of the user to be checked 
	 * @return	the login state of the user
	 */
	public boolean userLoggedIn(String username) {
		for(int i=0;i<messageListener.size();i++){
			if(messageListener.get(i).username.equals(username))
				return messageListener.get(i).loggedIn;
		}
		return false;
	}
	
	/**
	 * @return the number of users that are logged in
	 */
	public int getNumberLoggedIn() {
		int num = 0;
		
		for(int n=0; n<messageListener.size();n++){
			if (messageListener.get(n).loggedIn)
				num++;
		}
		
		return num;
	}
	
	/**
	 * distributes messages to other clients
	 * 
	 * @param msg Message Object containing the message
	 */	
	public void distributeMsg(Message msg){
		boolean temp=false;
		int i=serverObject.networkListener.messageListener.size()-1;				// get number of clients connected to the server
		for(int n=0; n<=i;n++){
			temp=true;
			if (msg instanceof SysMessage) {
				temp=true;
			} else if (msg instanceof TextMessage) {
				if(serverObject.userManager.users.get(serverObject.networkListener.messageListener.get(n).username).compareDir(((TextMessage)msg).getRoomDir())){
					temp=true;
				}
			}
			if((serverObject.networkListener.messageListener.get(n).username.equals(msg.getSenderUsername())==false)&&(serverObject.networkListener.messageListener.get(n).loggedIn==true)){
				if(temp){
				try {
					serverObject.networkListener.messageListener.get(n).connector.sendMessageObject(msg);
				} catch (IOException e) {
					e.printStackTrace();
					serverObject.ui.postError(String.format("Error sending message to %s.", serverObject.networkListener.messageListener.get(n).username));
				}	
			}
			}
		}
		
		return;
	}
	
	/**
	 * close all message listeners
	 */
	public void close(){
		for(int i=0;i<messageListener.size();i++){
			messageListener.get(i).close(false);
		}
		running=false;
		server=null;
		return;
	}

	/**
	 * sends a text message to all clients
	 * 
	 * @param msg string containing the message
	 */
	public void sendServerTextMessage(String msg){
		if(serverObject.isStarted){
			TextMessage tempMsg=new TextMessage(msg);			//creates the message
			tempMsg.setSenderUsername("SERVER");
			tempMsg.setRoomDir(serverObject.roomManager.foyer.getDir());
			tempMsg.setSent(new Date());
			distributeMsg(tempMsg);								//distributes it
		}
		return;
	}
}
